#include "Avalon/IO/PipedWriter.hpp"
#include "Avalon/IO/IOException.hpp"
#include "Avalon/IO/PipedReader.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::Threading;
using namespace boost;

PipedWriter::PipedWriter(PipedReader* destination)
    : m_writeMutex(destination->m_writeMutex),
      m_destination(destination) {
  lock_guard<mutex> readLock(m_destination->m_readMutex);
  lock_guard<mutex> writeLock(*m_destination->m_writeMutex);
  m_destination->Connect(this);
}

PipedWriter::~PipedWriter() {
  Break();
}

void PipedWriter::Break() {
  unique_lock<mutex> writeLock(*m_writeMutex);
  if(m_destination == NULL) {
    return;
  }
  unique_lock<mutex> readLock(m_destination->m_readMutex);
  PipedReader* destination = m_destination;
  InternalBreak();
  destination->InternalBreak(readLock, writeLock);
}

void PipedWriter::Write(const void* data, int size, Out<Async<void> > result) {
  unique_lock<mutex> writeLock(*m_writeMutex);
  if(m_destination == NULL) {
    writeLock.unlock();
    result->SetException(IOException("Pipe broken."));
    return;
  }
  unique_lock<mutex> readLock(m_destination->m_readMutex);
  m_destination->Write(data, size, readLock, writeLock);
  result->SetResult();
}

void PipedWriter::Write(const Buffer& data, Out<Async<void> > result) {
  Write(data.GetData(), data.GetSize(), Store(result));
}

void PipedWriter::InternalBreak() {
  m_destination = NULL;
}
